/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package genomemap.model;

import com.google.common.collect.ImmutableList;
import commons.core.OrderModel;
import commons.core.Singletons;
import commons.util.ExceptionUtil;
import org.apache.commons.lang.builder.ToStringBuilder;

import java.util.Collections;
import java.util.List;

/**
 * This model corresponds to the hybridization experiment explained in the paper Kececioglu, J.;
 * Shete, S. & Arnold, J. RECOM 2000.
 *
 * @author Susanta Tewari
 * @since Jul 30, 2011
 */
public class KSA00 implements OrderModel<String, KSA00> {

    /** Field description */
    private final ImmutableList<String> probes;


    // number of probes + 1. starting window, consecutive windows and the end window

    /** Field description */
    private final ImmutableList<Double> probeSpacing;

    /* length of the chromosome */

    /** Field description */
    private final int chLength;

    /** Field description */
    private final int clone_length;

    /** Field description */
    private final double false_positive_prob;

    /** Field description */
    private final double false_negative_prob;

    /**
     * Constructs ...
     *
     *
     * @param probes
     * @param chLength
     * @param clone_length
     * @param false_positive_prob
     * @param false_negative_prob
     */
    public KSA00(final List<String> probes, final int chLength, final int clone_length,
                 final double false_positive_prob, final double false_negative_prob) {

        this(probes, estimateDefaultProbeSpacings(probes.size(), chLength, clone_length), chLength,
             clone_length, false_positive_prob, false_negative_prob);
    }

    /**
     * @param probes probes in this model in their specified order
     * @param probeSpacing starting window, consecutive windows and the end window
     * @param chromosome_length length of the chromosome under this model
     * @param clone_length length of a clone under this model; all clones are assumed to have the
     * length
     * @param false_positive_prob specified false positive probability
     * @param false_negative_prob specified false negative probability
     */
    @SuppressWarnings("TypeMayBeWeakened")
    public KSA00(final List<String> probes, final List<Double> probeSpacing,
                 final int chromosome_length, final int clone_length,
                 final double false_positive_prob, final double false_negative_prob) {

        this.probes       = ImmutableList.copyOf(probes);
        this.probeSpacing = ImmutableList.copyOf(probeSpacing);


        // check invariance: probeSpacing.size() == probes.size() + 1
        if (probeSpacing.size() != probes.size() + 1) {

            throw new IllegalArgumentException(
                "invariance failed: probeSpacing.size() == probes.size() + 1");
        }

        this.chLength            = chromosome_length;
        this.clone_length        = clone_length;
        this.false_positive_prob = false_positive_prob;
        this.false_negative_prob = false_negative_prob;
    }

    /**
     * Method description
     *
     *
     * @param probe_count
     * @param chLength
     * @param clone_length
     *
     * @return
     */
    private static List<Double> estimateDefaultProbeSpacings(final int probe_count,
            final int chLength, final int clone_length) {

        final double space = (chLength - probe_count * clone_length) / (probe_count + 1);

        return Collections.nCopies(probe_count + 1, space);
    }

    /**
     * Creates a new instance of model using the current instance and the specified parameters.
     *
     * @param probeSpacing new spacing between the probes for the current model
     * @return a new instance of the model
     */
    public KSA00 create(final List<Double> probeSpacing) {

        if ((probes.size() + 1) != probeSpacing.size()) {
            ExceptionUtil.throwIllegalArgEx(getClass(), "KSA00_NewProbeSpacing_Size_Mismatch");
        }

        return new KSA00(probes, probeSpacing, chLength, clone_length, false_positive_prob,
                         false_negative_prob);
    }

    /**
     * Creates a new instance of the model for the given probes reusing the rest of the model
     * parameters. The probe spacings are re-calculated.
     *
     * @param probes sub set of probes from this model
     * @return a new instance of model with the specified set of probes
     */
    @Override
    public KSA00 newOrder(final List<String> probes) {

        if (probes == null) {
            ExceptionUtil.throwIllegalArgExNull("probes");
        }

        if (probes.isEmpty()) {
            ExceptionUtil.throwIllegalArgEx(getClass(), "KSA00_changeProbes_ProbesEmpty");
        }

        return new KSA00(probes, chLength, clone_length, false_positive_prob, false_negative_prob);
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public ImmutableList<String> getProbes() {
        return probes;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    @Override
    public ImmutableList<String> getOrder() {
        return probes;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public ImmutableList<Double> getProbeSpacing() {
        return probeSpacing;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public int getChromosomeLength() {
        return chLength;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public int getCloneLength() {
        return clone_length;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public double getFalsePositiveProbability() {
        return false_positive_prob;
    }

    /**
     * Method description
     *
     *
     * @return
     */
    public double getFalseNegativeProbability() {
        return false_negative_prob;
    }

    /**
     * Contains information on model parameters.
     *
     * @return information on model parameters
     */
    @Override
    public String toString() {

        return new ToStringBuilder(this, Singletons.TO_STRING_STYLE).append("chromosome-length",
                                   chLength).append("clone-length",
                                       clone_length).append("false-negative-prob",
                                           false_negative_prob).append("false-positive-prob",
                                               false_positive_prob).append("probes",
                                                   probes).append("probe-spacing",
                                                       probeSpacing).toString();
    }
}
